﻿
#pragma once
#define  WIN32_LEAN_AND_MEAN
#include "stdafx.h"
#include "SDKWindow.h"
#include "H_Define.h"
#include <shlwapi.h>
#include <Richedit.h>
#include <CommCtrl.h>
#include "_H_SOCKET.h"

CSDKWindow WndLogin, WndMain;    //对话框对象
HWND hWndLogin, hWndMain;        //对话框句柄
HWND hWndRichEdit;				 //RichEdit句柄
HWND hOnlineList;				 //在线列表框句柄
CNBSocket sckLogin;
TCHAR g_szNickname[MAX_NICKNAMELEN];

INT_PTR CALLBACK LoginProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK MainProc(HWND, UINT, WPARAM, LPARAM);
DWORD CALLBACK cbUpdateOnline();
DWORD WINAPI RecvThread(LPVOID lpParam);
bool DisposeRecv(SOCKDATA s);
void AddChatText(LPCTSTR str);
bool UpdateOnlineList(SOCKDATA sdOnline);
bool LoginThread();
void ShowTip(LPCTSTR strTip);
void ShowTipA(LPCSTR strTip);
void MoveToMiddle(HWND hWnd);

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
{
	//创建对话框
	hWndLogin = WndLogin.Create(hInstance, IDD_LOGIN_DLG, NULL, LoginProc);
	WndLogin.ShowWindow(SW_SHOW);
	WndLogin.UpdateWindow();

	//进入消息循环
	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{
		if ( !IsDialogMessage (hWndLogin, &msg) )
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	return msg.wParam;
}

/************************************************************************/
/* 登录对话框消息处理											        */
/************************************************************************/
INT_PTR CALLBACK LoginProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	bool bLogin = false;
	switch(uMsg)
	{
	case WM_INITDIALOG:
		SendDlgItemMessage(hWnd, IDC_NICKNAME_EDIT, WM_SETTEXT, NULL, (LPARAM)_T(DEFAULT_NICKNAME));   /*设置默认昵称*/
		SendDlgItemMessage(hWnd, IDC_IPADDRESS, WM_SETTEXT, NULL, (LPARAM)_T(DEFAULT_HOST));           /*设置默认服务器*/
		SendDlgItemMessage(hWnd, IDC_PORT_EDIT, WM_SETTEXT, NULL, (LPARAM)_T(DEFAULT_PORT));           /*设置默认端口*/
		break;
	case WM_SHOWWINDOW:
		WndLogin.SetWindowTextW(_T(APP_TITTLE));
		MoveToMiddle(WndLogin.GetWindowHandle());
		break;
	case WM_PAINT:
		break;
	case WM_CLOSE:
		PostQuitMessage(0);
		break;
	case WM_COMMAND:
		switch(LOWORD(wParam))
		{
		case IDLOGIN:  /*登录聊天室*/
			
			//登录
			EnableWindow(GetDlgItem(hWndLogin, IDC_NICKNAME_EDIT), FALSE);
			EnableWindow(GetDlgItem(hWndLogin, IDC_IPADDRESS), FALSE);
			EnableWindow(GetDlgItem(hWndLogin, IDC_PORT_EDIT), FALSE);
			EnableWindow(GetDlgItem(hWndLogin, IDLOGIN), FALSE);
			bLogin = LoginThread();
			EnableWindow(GetDlgItem(hWndLogin, IDC_NICKNAME_EDIT), TRUE);
			EnableWindow(GetDlgItem(hWndLogin, IDC_IPADDRESS), TRUE);
			EnableWindow(GetDlgItem(hWndLogin, IDC_PORT_EDIT), TRUE);
			EnableWindow(GetDlgItem(hWndLogin, IDLOGIN), TRUE);

			//加载主窗口
			if (bLogin == true)
			{
				hWndMain = WndMain.Create(NULL, IDD_MAIN_DLG, GetDesktopWindow(), MainProc);
				if ( !hWndMain)
				{
					ShowTip(_T("加载窗口错误"));
					MessageBox(hWndLogin, _T("加载窗口错误！"), _T("错误"), MB_ICONERROR);
				}

				WndMain.ShowWindow(SW_SHOW);
				WndLogin.ShowWindow(SW_HIDE);

				//创建接收线程
				CreateThread(0, 0, RecvThread, NULL, 0, 0);
			}

			break;
		case IDCANCEL: /*退出*/
			if (MessageBox(WndLogin.GetWindowHandle(), _T("是否确认要退出？"), _T(APP_TITTLE), MB_YESNO | MB_ICONQUESTION ) == IDYES)
			{
				PostQuitMessage(0);
			}
			break;
		}
		break;
	default:
		break;
	}
	
	return 0;
}

/************************************************************************/
/* 聊天室对话框消息处理											        */
/************************************************************************/
INT_PTR CALLBACK MainProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	HMODULE hMdlREdit;
	LONG lStyle;
	
	DWORD dwStyle;
	LVCOLUMN cn;

	SOCKDATA sdGetOnline;
	SOCKDATA sdGotOnlineList;
	char *lpBuff_GetOnline = new char[sizeof(SOCKDATA)];

	switch(uMsg)
	{
	case WM_INITDIALOG:
		//加载库
		hMdlREdit = LoadLibrary(_T("riched20.dll"));
		if (!hMdlREdit)
		{
			MessageBox(hWndLogin, _T("糟糕了！！少了个DLL！"), _T("错误"), MB_ICONERROR);
			PostQuitMessage(0);
		}

		//创建RichEdit
		hWndRichEdit = CreateWindowEx(
									WS_EX_CLIENTEDGE, 
									_T("RICHEDIT20A"), 
									NULL, 
									ES_MULTILINE | WS_CHILD |WS_VISIBLE |WS_VSCROLL | ES_NOHIDESEL | ES_READONLY, 
									230, 30, 414, 315, 
									hWnd, 
									NULL, NULL, NULL 
									);
		if (!hWndRichEdit)
		{
			MessageBox(hWndLogin, _T("程序出现致命性错误。大喊三声我爸是李刚，程序将会重新启动。"), _T("错误"), MB_ICONERROR);
			PostQuitMessage(0);
		} else {

			//设置RichEdit背景色
			SendMessage(hWndRichEdit, EM_SETBKGNDCOLOR, NULL, RGB(240, 240, 240));

			//URL自动识别支持
			SendMessage(hWndRichEdit, EM_AUTOURLDETECT, (WPARAM)TRUE, 0);

			//添加在线列表
			SendDlgItemMessage(hWnd, IDC_TARGET_COMBO, CB_ADDSTRING, NULL, (LPARAM)_T("所有人"));
			SendDlgItemMessage(hWnd, IDC_TARGET_COMBO, CB_ADDSTRING, NULL, (LPARAM)_T("A"));
			SendDlgItemMessage(hWnd, IDC_TARGET_COMBO, CB_ADDSTRING, NULL, (LPARAM)_T("C"));
		}

		break;
	case WM_COMMAND:
		switch(LOWORD(wParam))
		{
		case IDC_TARGET_COMBO:  //聊天对象下拉框
			if (HIWORD(wParam) == CBN_SELCHANGE)
			{

				HWND hComboTarget = GetDlgItem(hWndMain, IDC_TARGET_COMBO);   //取得句柄
				int nCurSel = SendMessage(hComboTarget, CB_GETCURSEL, 0, 0);  //取得当前索引值

				Trace(_T("当前索引是：%d\n"), nCurSel);

				//如果聊天对象是所有人，则私聊复选框变为不可用
				if (nCurSel == 0)
				{
					EnableWindow(GetDlgItem(hWndMain, IDC_ISPRIVATE_CHECK), FALSE);
				} else {
					EnableWindow(GetDlgItem(hWndMain, IDC_ISPRIVATE_CHECK), TRUE);
				}
			}
			break;
		case IDC_CLEAR_BTN:
			SendDlgItemMessage(hWndMain, IDC_SENDMSG_EDIT, WM_SETTEXT, 0, (LPARAM)_T(""));
			SetFocus(GetDlgItem(hWndMain, IDC_SENDMSG_EDIT));
			break;
		case IDC_SEND_BTN:

			//获取将要发送的信息
			TCHAR szSendMsg[MAX_RECV_BUFFLEN];
			memset(szSendMsg, 0, MAX_RECV_BUFFLEN);
			SendDlgItemMessage(hWndMain, IDC_SENDMSG_EDIT, WM_GETTEXT, (WPARAM)MAX_RECV_BUFFLEN, (LPARAM)szSendMsg);
			
			//构造信息数据包
			SOCKDATA sdChatMsg;
			sdChatMsg.CommandType = CMD_CHATMSG_CLIENT;
			wcsncpy(sdChatMsg.lpNickname, g_szNickname, MAX_NICKNAMELEN);
			wcsncpy(sdChatMsg.lpMsg, szSendMsg, MAX_RECV_BUFFLEN);
			sdChatMsg.crTextColor = RGB(255, 0, 0);

			HWND hComboTarget = GetDlgItem(hWndMain, IDC_TARGET_COMBO);				//取得聊天对象下拉框句柄
			HWND hCheckPrivate = GetDlgItem(hWndMain, IDC_ISPRIVATE_CHECK);			//是否私聊复选框句柄
			int nCurSel_Target = SendMessage(hComboTarget, CB_GETCURSEL, 0, 0);		//取得聊天对象下拉框当前索引值
			int nCurSel_IsPrivate = SendMessage(hCheckPrivate, BM_GETCHECK, 0, 0);	//取得是否私聊复选框当前索引值

			//如果聊天对象是所有人，则ChatMode为0
			if (nCurSel_Target == 0)
			{
				sdChatMsg.ChatMode = 0;
				wcsncpy(sdChatMsg.lpTarget, _T("所有人"), MAX_NICKNAMELEN);
			} else {
				TCHAR szTarget[MAX_NICKNAMELEN];
				GetWindowText(hComboTarget, szTarget, MAX_NICKNAMELEN);
				wcsncpy(sdChatMsg.lpTarget, szTarget, MAX_NICKNAMELEN);

				if (nCurSel_IsPrivate == BST_CHECKED)
				{
					sdChatMsg.ChatMode = 1;
				} else {
					sdChatMsg.ChatMode = 0;
				}
			}

			//发送数据包
			EnableWindow(GetDlgItem(hWndMain, IDC_SEND_BTN), FALSE);
			if (sckLogin.Send((char*)&sdChatMsg, sizeof(SOCKDATA), 0) == SOCKET_ERROR)
			{
				AddChatText(_T("系统：发送信息失败！"));
				return -1;
			}

			EnableWindow(GetDlgItem(hWndMain, IDC_SEND_BTN), TRUE);
			SendDlgItemMessage(hWndMain, IDC_SENDMSG_EDIT, WM_SETTEXT, 0, (LPARAM)_T(""));
			SetFocus(GetDlgItem(hWndMain, IDC_SENDMSG_EDIT));

			break;
		}
		break;
	case WM_SHOWWINDOW:

		//设置聊天对象列表默认项
		SendDlgItemMessage(hWndMain, IDC_TARGET_COMBO, CB_SETCURSEL, 0, 0);

		//初始化在线列表
		hOnlineList = GetDlgItem(hWndMain, IDC_ONLINE_LIST);
		lStyle = GetWindowLong(hOnlineList, GWL_STYLE);				//获取当前窗口style
		lStyle &= ~LVS_TYPEMASK;									//清除显示方式位
		lStyle |= LVS_REPORT;										//报表方式
		SetWindowLong(hOnlineList, GWL_STYLE, lStyle);				//设置style

		dwStyle = SendMessage(hOnlineList, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);

		dwStyle |= LVS_EX_FULLROWSELECT;		//选中某行使整行高亮（只适用与report风格的listctrl）
		dwStyle |= LVS_EX_GRIDLINES;			//网格线（只适用与report风格的listctrl）
		dwStyle |= LVS_EX_SUBITEMIMAGES;		//添加图标

		//设置扩展风格
		SendMessage(hOnlineList, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, (LPARAM)dwStyle);
		
		//添加列头
		cn.mask = LVCF_WIDTH | LVCF_TEXT;
		cn.fmt = LVCFMT_RIGHT;
		cn.cx = 90;
		cn.pszText = _T("昵称");
		SendDlgItemMessage(hWndMain, IDC_ONLINE_LIST, LVM_INSERTCOLUMN, 1, (LPARAM)&cn);

		cn.mask = LVCF_WIDTH | LVCF_TEXT;
		cn.fmt = LVCFMT_RIGHT;
		cn.cx = 110;
		cn.pszText = _T("登录时间");
		SendDlgItemMessage(hWndMain, IDC_ONLINE_LIST, LVM_INSERTCOLUMN, 2, (LPARAM)&cn);

		//获取在线列表

		sdGetOnline.CommandType = CMD_GETONLINELIST;
		if (sckLogin.Send((char*)&sdGetOnline, sizeof(SOCKDATA), 0) == SOCKET_ERROR)
		{
			MessageBox(hWndMain, _T("请求在线列表失败！"), _T("错误"), MB_ICONERROR);
			return -1;
		}


		//接收服务端返回的数据
		if (sckLogin.Recv(lpBuff_GetOnline, sizeof(SOCKDATA), 0) == SOCKET_ERROR)
		{
			MessageBox(hWndMain, _T("请求在线列表失败！无法接收服务器数据。"), _T("错误"), MB_ICONERROR);
			return -1;
		} else {
			memcpy(&sdGotOnlineList, lpBuff_GetOnline, sizeof(SOCKDATA));

			//更新在线列表
			UpdateOnlineList(sdGotOnlineList);

			//启动定时器，更新在线列表
			SetTimer(hWndMain, IDT_UPDATEONLINE, TIMER_UPDATEONLINE, (TIMERPROC)cbUpdateOnline);
		}

		break;
	case WM_CLOSE:
		if(MessageBox(hWndMain, _T("确认要退出聊天室吗？"), _T("提示"), MB_ICONQUESTION | MB_YESNO) == IDYES)
		{
			PostQuitMessage(0);
		}
		break;
	}

	return 0;
}

/************************************************************************/
/* 处理接收到的信息														        */
/************************************************************************/
bool DisposeRecv(SOCKDATA s)
{
	TCHAR szRecvMsg[MAX_RECV_BUFFLEN];
	memset(szRecvMsg, 0, MAX_RECV_BUFFLEN);
	switch(s.CommandType)
	{

	case CMD_GETONLINELIST:
		TraceA("更新在线列表...");
		UpdateOnlineList(s);
		break;

	case CMD_CHATMSG_CLIENT:

		//组织聊天信息数据包
		if (s.ChatMode == 0)
		{
			_snwprintf(szRecvMsg, MAX_RECV_BUFFLEN, _T("【%s】 对 【%s】 说  %u:%u:%u\n%s"), s.lpNickname, s.lpTarget, s.tmSend.wHour, s.tmSend.wMinute, s.tmSend.wSecond, s.lpMsg);
			AddChatText(szRecvMsg);
			SendMessage(hWndRichEdit, WM_VSCROLL, (WPARAM)SB_BOTTOM, 0);
		}

		break;
	default:
		TraceA("未知信息！");
		break;
	}

	return true;
}



/************************************************************************/
/* 更新聊天文本														    */
/************************************************************************/
void AddChatText(LPCTSTR str)
{
	//获取接收到的信息
	TCHAR szRecvedMsg[MAX_RECV_BUFFLEN];
	SendMessage(hWndRichEdit, WM_GETTEXT, (WPARAM)MAX_RECV_BUFFLEN, (LPARAM)szRecvedMsg);

	TCHAR szTemp[MAX_RECV_BUFFLEN];
	_snwprintf(szTemp, MAX_RECV_BUFFLEN, _T("%s\n%s"), szRecvedMsg, str);
	SendMessage(hWndRichEdit, WM_SETTEXT, 0, (LPARAM)szTemp);
}

/************************************************************************/
/* 登录过程														        */
/************************************************************************/
bool LoginThread()
{
	ShowTip(_T("正在连接服务器..."));

	void UpdateUI();
	void ShowWnd();
	TCHAR lpszBuffer[64];
	

	//初始化socket
	if (sckLogin.Create(AF_INET, SOCK_STREAM, IPPROTO_TCP) == false)
	{
		Trace(_T("创建socket失败！错误代号：%d\n"), sckLogin.GetLastError());
		ShowTip(_T("初始化连接失败！"));
		return false;
	}
	
	char lpszIPAddress[INET_ADDRSTRLEN + 1];
	wchar_t nickname_buffer[40];
	wchar_t ipaddr_buffer[40];
	wchar_t port_buffer[40];

	memset(lpszIPAddress, 0, INET_ADDRSTRLEN + 1);

	SendDlgItemMessage(hWndLogin, IDC_NICKNAME_EDIT, WM_GETTEXT, INET_ADDRSTRLEN, (LPARAM)nickname_buffer);
	SendDlgItemMessage(hWndLogin, IDC_IPADDRESS, WM_GETTEXT, INET_ADDRSTRLEN, (LPARAM)ipaddr_buffer);
	SendDlgItemMessage(hWndLogin, IDC_PORT_EDIT, WM_GETTEXT, INET_ADDRSTRLEN, (LPARAM)port_buffer);

	Trace(_T("昵称:%s | 服务器：%s. | 端口：%s\n"), nickname_buffer, ipaddr_buffer, port_buffer);

	u_short uPort = _wtoi(port_buffer);
	WideCharToMultiByte(CP_ACP, 0, ipaddr_buffer, -1, lpszIPAddress, INET_ADDRSTRLEN + 1, 0, 0);

	//连接服务器
	bool bConn = sckLogin.Connect(lpszIPAddress, uPort);
	int nErr = sckLogin.GetLastError();
	Trace(_T("连接结果：%d\n"), bConn);
	if (bConn == false)
	{
		LoadString(NULL, 104, lpszBuffer, sizeof(lpszBuffer));
		ShowTip(_T("连接服务器失败"));
		sckLogin.Close();
		MessageBox(hWndLogin, lpszBuffer, _T("错误"), MB_ICONWARNING );
		Trace(_T("socket错误：%d"), nErr);
		return false;
	}

//////////////////////////////////////////////////////////////////////////
	ShowTip(_T("正在发送登录请求..."));

	SOCKDATA sdLogin;
	
	//获取昵称，构造登录数据包
	sdLogin.CommandType = CMD_LOGIN_CLIENT;
	wcsncpy(sdLogin.lpNickname, nickname_buffer, MAX_NICKNAMELEN);
	wcsncpy(g_szNickname, nickname_buffer, MAX_NICKNAMELEN);

	//发送登录请求
	int nResult = sckLogin.Send((char*)&sdLogin, sizeof(SOCKDATA), 0);
	if (nResult == SOCKET_ERROR)
	{
		ShowTip(_T("登录失败"));
		sckLogin.Close();
		LoadString(NULL, 104, lpszBuffer, sizeof(lpszBuffer));
		MessageBox(hWndLogin, lpszBuffer, _T("错误"), MB_ICONWARNING);
		return false;
	} else {

		char* lpRecvBuff = new char[sizeof(SOCKDATA)];

		//接收服务器请求
		int iResult = sckLogin.Recv(lpRecvBuff, sizeof(SOCKDATA), 0);
		if (iResult != SOCKET_ERROR && iResult > 0)
		{
			SOCKDATA ag;
			memcpy(&ag, lpRecvBuff, sizeof(SOCKDATA));

			if ( ag.bAgree == false )
			{
				ShowTip(ag.lpErrorStr);
				sckLogin.Close();
				TCHAR *lpErrStr = ag.lpErrorStr;
				MessageBox(hWndLogin, lpErrStr, _T("登录失败"), MB_ICONWARNING);
				return false;
			} 

			ShowTip(_T("登录成功！"));
			return true;
		}
		delete[] lpRecvBuff;
		return false;
	}
	return false;
}

//接受信息线程
DWORD WINAPI RecvThread(LPVOID lpParam)
{
	while (true)
	{
		char *lpszBuffRecv = new char[sizeof(SOCKDATA)];
		if (sckLogin.Recv(lpszBuffRecv, sizeof(SOCKDATA), 0) != SOCKET_ERROR)
		{
			SOCKDATA s;
			memcpy(&s, lpszBuffRecv, sizeof(SOCKDATA));
			DisposeRecv(s);
		}
		delete[] lpszBuffRecv;
	}
}

/************************************************************************/
/* 把窗口移动到相对于父窗口的中央								        */
/************************************************************************/
void MoveToMiddle(HWND hWnd)
{
	RECT rt;
	int cx,cy,h,w;

	::GetWindowRect(hWnd, &rt);

	h = rt.bottom - rt.top;
	w = rt.right - rt.left;
	cx = GetSystemMetrics (SM_CXSCREEN);
	cy = GetSystemMetrics (SM_CYSCREEN);
	::MoveWindow (hWnd, cx/2-w/2, cy/2-h/2, w, h, TRUE);
}

/************************************************************************/
/* 显示状态文本															*/
/************************************************************************/
void ShowTip(LPCTSTR strTip)
{
	SetWindowText(GetDlgItem(hWndLogin, IDC_TIP_STATIC), strTip);
}
void ShowTipA(LPCSTR strTip)
{
	SetWindowTextA(GetDlgItem(hWndLogin, IDC_TIP_STATIC), strTip);
}

//更新在线列表
bool UpdateOnlineList(SOCKDATA sdOnline)
{
	//清空列表
	SendMessage(hOnlineList, LVM_DELETEALLITEMS, 0, 0);

	//遍历客户端，添加到列表
	for (int i = 0; i < MAX_CLIENTS; i++)
	{
		if (_tcslen(sdOnline.nOnlineHost[i].lpNickName) != 0)
		{
			Trace(_T("[ol]nickname:%s\n"), &sdOnline.nOnlineHost[i].lpNickName);
			TCHAR szTmp[MAX_NICKNAMELEN];
			memset(szTmp, 0, MAX_NICKNAMELEN);

			/************************************************************************/
			/* 昵称	                                                                */
			/************************************************************************/
			LVITEM lvi = {0};

			lvi.mask = LVIF_TEXT;
			lvi.iItem = i;
			lvi.iSubItem = 0;
			lvi.pszText = sdOnline.nOnlineHost[i].lpNickName;
			SendMessage (hOnlineList, LVM_INSERTITEM, NULL, (LPARAM)&lvi);

			//获取登录时间
			TCHAR szLoginTime[32];
			memset(szLoginTime, 0, 32);
			_snwprintf(szLoginTime, 32, _T("%u-%u-%u %u:%u:%u"), 
				sdOnline.nOnlineHost[i].tm.wYear,
				sdOnline.nOnlineHost[i].tm.wMonth,
				sdOnline.nOnlineHost[i].tm.wDay,
				sdOnline.nOnlineHost[i].tm.wHour,
				sdOnline.nOnlineHost[i].tm.wMinute,
				sdOnline.nOnlineHost[i].tm.wSecond);

			lvi.mask = LVIF_TEXT;
			lvi.iItem = i;
			lvi.iSubItem = 1;
			lvi.pszText = szLoginTime;
			SendMessage (hOnlineList, LVM_SETITEMTEXT, (WPARAM)i-1, (LPARAM)&lvi);

			TCHAR szCaption[256] = {0};
			_snwprintf(szCaption, 256, _T("在线人数(%d)"), i);
			SendDlgItemMessage(hWndMain, IDC_ONLINE_FRAME, WM_SETTEXT, NULL, (LPARAM)szCaption);
		}
	}
	return true;
}

//定时器回调函数：更新在线列表
DWORD CALLBACK cbUpdateOnline()
{
	SOCKDATA adGetOlListRequest;
	SOCKDATA adGetOlList;
	adGetOlListRequest.CommandType = CMD_GETONLINELIST;

	//发送请求
	if (sckLogin.Send((char*)&adGetOlListRequest, sizeof(SOCKDATA), 0) == SOCKET_ERROR)
	{
		TraceA("获取在线列表失败：发送请求失败！");
		return -1;
	}

	return 0;

}